package de.fhkl.gatav.ut.raindrops_1;

import de.fhkl.gatav.ut.raindrops_1.R;
import java.util.LinkedList;
import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.RectF;

/****
 * bubblesView: Manages display handling of app. Implements SurfaceHolder.Callback to access certain display properties
 */
public class RaindropsView extends SurfaceView implements SurfaceHolder.Callback {

	private SurfaceHolder surfaceHolder = null; //Surface to hijack
	private GameLoop gameLoop; //Display refresh thread
	private LinkedList<Drop> drops = new LinkedList<Drop>(); //Our bubble objects
	private float BUBBLE_FREQUENCY = 0.7f; //Bubble generation rate
	//Certain paint properties and objects
	private Bitmap backgroundBitmap;
	private Bitmap raindropBitmap;
	
/****
 * Constructor
 * @param context
 * @param attrs
 */
	public RaindropsView(Context context, AttributeSet attrs) {
		super(context, attrs);
		getHolder().addCallback((Callback) this);	//Register this class as callback handler for the surface
		backgroundBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.cloudyheaven);
		raindropBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.drop);
	}

/****
 * drawScreen: Paints background and all bubbles	
 * @param c: Canvas to be drawn on
 */
	private void drawScreen(Canvas c) {
		Rect nullRect = null;
		Paint nullPaint = null;
		c.drawBitmap(backgroundBitmap, 
					 nullRect, 
					 new RectF(0,0,c.getWidth(),c.getHeight()), 
					 nullPaint); //Draw background
		
		for (Drop drop : drops) {									//Draw bubbles
			drop.draw(c);
		}
	}

/****
 * calculateDisplay: Generates new bubble, moves bubble, removes unused bubbles	
 * @param canvas: Canvas to calculate moves for
 * @param numberOfFrames: No. of frames since last call
 */
	private void calculateDisplay(Canvas canvas, float numberOfFrames) {
		randomlyAddBubbles(canvas.getWidth(), canvas.getHeight(), numberOfFrames);		//Add a new bubble
		LinkedList<Drop> dropsToRemove = new LinkedList<Drop>();	//Move all bubbles
		for (Drop drop : drops) {
			drop.move(canvas, numberOfFrames);
			if (drop.outOfRange(canvas))									//and keep display leavers in mind
				dropsToRemove.add(drop);
		}
		
		for (Drop drop : dropsToRemove) {							//Remove all bubbled up
			drops.remove(drop);
		}
	}
 
/****
 * randomlyAddBubbles: Adds a bubble at random. Probability rises with the number of frames passed	
 * @param screenWidth ...
 * @param screenHeight ...
 * @param numFrames: No. of frames since last call
 */
	public void randomlyAddBubbles(int screenWidth, int screenHeight, float numFrames) {
		//Create a bubble every time the number of frame threshold is exceeded
		if (Math.random()>BUBBLE_FREQUENCY*numFrames)
			return;
      
		drops.add(new Drop((int)(screenWidth*Math.random()), 				//x pos at random
							 (int)(screenHeight*Math.random()),				//y pos under bottom of screen
							 (int)((Bubble.MAX_SPEED/5)*Math.random()+0.1),	//This avoids bubbles of speed 0
							 raindropBitmap));
	}
	
/****
 * Private display loop thread	
 */
	private class GameLoop extends Thread {
		private long msPerFrame = 1000/25;	//Frame rate
		public boolean running = true;		//Control flag for start / stop mechanism
		private long frameTime = 0;			//Clock to timing control

/****
 * run is the standard routine called, when a thread is started via the start() method	
 */		
		public void run() {
			Canvas canvas = null;
			long thisFrameTime;
			long lastFrameTime = System.currentTimeMillis();
			float framesSinceLastFrame;
			final SurfaceHolder surfaceHolder = RaindropsView.this.surfaceHolder;
			
			frameTime = System.currentTimeMillis();
			while (running) {
				try {
					canvas = surfaceHolder.lockCanvas();	//Get the canvas exclusively
					synchronized (surfaceHolder) {			//Must be executed exclusively
						drawScreen(canvas);					//Draw bubbles
					}
					thisFrameTime = System.currentTimeMillis();		//Calculate the exact no. of frames since last loop
					framesSinceLastFrame = (float)(thisFrameTime - lastFrameTime)/msPerFrame;
					lastFrameTime = thisFrameTime;
					calculateDisplay(canvas, framesSinceLastFrame);	//
				} finally {
					if (canvas != null)
						surfaceHolder.unlockCanvasAndPost(canvas);
				}
			}
		}
	}
					
/****
 * Interfcae implementation	
 */
	
/****
 * Called when display is up	
 */
	public void surfaceCreated(SurfaceHolder holder) {
		surfaceHolder = holder;
		synchronized (this) {				//Must be executed exclusively
			if (gameLoop == null) {
				gameLoop = new GameLoop();	//Start animation here
				gameLoop.start();
			}
		}
	}
	
/****
 * Not used	
 */
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		//Nothing to do
	}
	
/****
 * Called before display will be brought down	
 */
	public void surfaceDestroyed(SurfaceHolder holder) {
		synchronized (this) {				//Must be executed exclusively
			boolean retry = true;
			if (gameLoop != null) {			//Stop the loop
				gameLoop.running = false;
				while (retry) {
					try {
						gameLoop.join();	//Catch the thread
						retry = false;
					} catch (InterruptedException e) {
					}
				}
			}
			gameLoop = null;
		}
	}
}